/**
 * 
 */
package profile.result;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;


/**
 * @author ductrung.tran
 *
 */
public class ResultsBuilder {

	public static interface LineHandler {
		void handleLine(String line);
	}
	
	public static class ResultsLineHandler implements LineHandler {
		
		private ProfileResults testResults;
		
		public ResultsLineHandler(ProfileResults testResults) {
			this.testResults = testResults;
		}
		
		public void handleLine(String line) {
			try {
				String[] arrays = line.split(";");
				int step = Integer.parseInt(arrays[1]);
				
				int dotIndex = arrays[2].indexOf(".");
				String store = arrays[2].substring(0, dotIndex);
				StoreResult testResult = new StoreResult();
				testResult.setStore(store);
				MethodResult methodTestResult = new MethodResult();
				
				int openParentheseIndex = arrays[2].indexOf("(");
				if (openParentheseIndex == -1) {
					throw new RuntimeException("No ( found in " + arrays[2]);
				}
				String methodName = arrays[2].substring(dotIndex+1, openParentheseIndex);
				
				//String[] calledMethod = arrays[1].substring(dotIndex+1).split("\\(");
				int closeParentheseIndex = arrays[2].lastIndexOf(")");
				if (closeParentheseIndex == -1) {
					throw new RuntimeException("No ) found in " + arrays[2]);
				}
				String[] paramArray = getParamArray(arrays[2].substring(openParentheseIndex+1, closeParentheseIndex));
				//System.out.println(paramArray.length);
				StringBuilder method = new StringBuilder(methodName).append('(');
				StringBuilder params = new StringBuilder();
				if (paramArray.length > 0) {
					for (String param : paramArray) {
						int colonIndex = param.indexOf(":");
						if (colonIndex == -1) {
							throw new RuntimeException("No : found in " + param);
						}
						
						method.append(param.substring(0, colonIndex)).append(',');
						params.append(param.substring(colonIndex+1)).append(',');
					}
					params.deleteCharAt(params.length()-1);
					method.deleteCharAt(method.length()-1);
				}
				method.append(')');
				
				methodTestResult.setName(method.toString());
				ParamResult paramTestResult = new ParamResult();
				paramTestResult.setName(params.toString());
				
				ParamResultItem item = new ParamResultItem();
				item.setStep(step);
				item.setExecTime(Long.parseLong(arrays[3].split(":")[1]));
				// bug temporaire
				if (arrays.length == 5) { // corba time not available du to no server call
					item.setCorbaTime(0);
					item.setServerTime(-1);
				} else {
					item.setCorbaTime(Long.parseLong(arrays[4].split(":")[1]));
					item.setServerTime(Long.parseLong(arrays[5].split(":")[1]));
				}
				paramTestResult.addResultItem(item);
				
				methodTestResult.addParamResult(paramTestResult);
				
				testResult.addMethodResult(methodTestResult);
				
				String context = arrays[0];
				ContextResult contextResult = new ContextResult(context);
				contextResult.addStoreResult(testResult);
				
				testResults.addContextResult(contextResult);
			} catch (Exception e) {
				throw new RuntimeException("Cannot handle line\n" + line, e);
			}
		}
		
		public String[] getParamArray(String paramsString) {
			//System.out.println(paramsString);
			ArrayList<String> paramList = new ArrayList<String>();
			doGetParamArray(paramsString, paramList);
			
			return paramList.toArray(new String[paramList.size()]);
		}

		private void doGetParamArray(String paramsString,
				ArrayList<String> paramList) {
			int virguleIndex = findLastVirguleIndex(paramsString);
			if (virguleIndex == -1) {
				if (paramsString.trim().length() > 0) {
					paramList.add(0, paramsString);
				}
			} else {
				paramList.add(0, paramsString.substring(virguleIndex+1));
				doGetParamArray(paramsString.substring(0, virguleIndex), paramList);
			}
		}

		private int findLastVirguleIndex(String paramsString) {
			int lastIndex = paramsString.length()-1;
			if (paramsString.endsWith("}")) { // complex object
				lastIndex = findOpenParentheseIndex(paramsString, paramsString.length()-1) - 1;
				//return paramsString.substring(0, openParentheseIndex).lastIndexOf(",");
			} else if (paramsString.endsWith("]")) { // array
				lastIndex = findOpenCrochetIndex(paramsString, paramsString.length()-1) - 1;
				//return paramsString.substring(0, openCrochetIndex).lastIndexOf(",");
			}
			
			return findLastIndexOf(',', paramsString, lastIndex);
			//return paramsString.substring(0, firstIndex).lastIndexOf(",");
		}

		private int findLastIndexOf(char searched, String paramsString,
				int firstIndex) {
			char c;
			for (int i = firstIndex; i >= 0; i--) {
				c = paramsString.charAt(i);
				if (c == searched && paramsString.charAt(i-1) != '\\') {
					return i;
				}
			}
			//throw new RuntimeException("No char " + searched + " found in " + paramsString + " before " + firstIndex);
			return -1;
		}

		public int findOpenCrochetIndex(String paramsString, int closeCrochetIndex) {
			int count = 1;
			for (int i = closeCrochetIndex-1; i >= 0; i--) {
				char c = paramsString.charAt(i);
				if (c == '[') {
					count--;
					if (count == 0) {
						return i;
					}
				} else if (c == ']') {
					count++;
				}
			}
			throw new RuntimeException("cannot find [ in " + paramsString);
		}

		public int findOpenParentheseIndex(String paramsString, int closeParentheseIndex) {
			int count = 1;
			for (int i = closeParentheseIndex-1; i >= 0; i--) {
				char c = paramsString.charAt(i);
				if (c == '{') {
					count--;
					if (count == 0) {
						return i;
					}
				} else if (c == '}') {
					count++;
				}
			}
			throw new RuntimeException("cannot find { in " + paramsString);
		}
	}
	
	public ProfileResults buildFromFile(String fileName) throws IOException {
		File file = new File(fileName);
		ProfileResults results = new ProfileResults(getName(file.getName()));
		
		doWithEachLine(file, new ResultsLineHandler(results));
		
		return results;
	}
	
	private String getName(String name) {
		// name must be in the form of ProfilingResult-IDLVersion-Server-Database-Timestamp.txt
		return name.substring("ProfilingResult-".length(), name.length() - ".txt".length());
	}

	private void doWithEachLine(File file, LineHandler visitor) throws IOException {
		doWithEachLine(new FileReader(file), visitor);
	}
	
	public void doWithEachLine(Reader reader, LineHandler visitor) throws IOException {
		BufferedReader buffReader = null;
		try {
			buffReader = new BufferedReader(reader);
			String line;
			while ((line = buffReader.readLine()) != null) {
				visitor.handleLine(line);
			}
		} finally {
			try {
				buffReader.close();
			} catch (Exception e) {};
		}
	}
}
